package island.gotenyama.wand.logic;

import island.gotenyama.wand.annotation.ValidationClass;
import island.gotenyama.wand.bean.AField;
import island.gotenyama.wand.bean.Conveyance;
import island.gotenyama.wand.bean.MessageKey;
import island.gotenyama.wand.exception.InputException;
import island.gotenyama.wand.validation.AValidation;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * 値を検証するロジッククラス
 * 
 * @author tk
 */
@Component
public class ValidationLogic implements Serializable, ApplicationContextAware {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * ApplicationContext
	 */
	ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	/**
	 * 検証用マップ
	 */
	private Map<AField, List<AValidation>> validtionListMap = new HashMap<AField, List<AValidation>>();

	/**
	 * 対象フィールドの検証を実施する。
	 * 
	 * @param aField　フィールド
	 * @param entry 検査対象
	 * @return 検査結果。検査ＯＫならばNULLを返す。
	 */
	public List<MessageKey> validate(AField aField, Object entry) {
		List<AValidation> validationList = validtionListMap.get(aField);
		synchronized (this) {
			if (validationList == null) {
				Field field = aField.getField();
				Annotation[] annArray = field.getAnnotations();
				for (Annotation ann : annArray) {
					ValidationClass validationClass = ann.annotationType().getAnnotation(ValidationClass.class);
					if (validationClass != null) {
						if (validationList == null) {
							validationList = new ArrayList<AValidation>();
						}
						validationList.add(applicationContext.getBean(validationClass.value()));
					}
				}
				validtionListMap.put(aField, validationList);
			}
		}
		List<MessageKey> messageKeyList = null;
		for (AValidation validation : validationList) {
			MessageKey messageKey = validation.validate(aField, entry);
			if (messageKey != null) {
				if (messageKeyList == null) {
					messageKeyList = new ArrayList<MessageKey>();
				}
				messageKeyList.add(messageKey);
			}
		}
		return messageKeyList;
	}

	/**
	 * メッセージが設定されている場合、例外をスローする。
	 * 
	 * @param messageKey メッセージ
	 * @throws InputException メッセージが設定されている場合にスロー
	 */
	public void evaluate(MessageKey messageKey) throws InputException {
		if (messageKey == null) {
			return;
		} else {
			final String errMsg = "input error exist";
			throw new InputException(new Conveyance(messageKey, errMsg));
		}
	}

	/**
	 * メッセージが設定されている場合、例外をスローする。
	 * 
	 * @param messageKeys メッセージ(配列）
	 * @throws InputException メッセージが設定されている場合にスロー
	 */
	public void evaluate(MessageKey... messageKeys) throws InputException {
		if (messageKeys == null) {
			return;
		} else {
			List<MessageKey> messageKeyList = new ArrayList<MessageKey>(messageKeys.length);
			for (MessageKey messageKey : messageKeys) {
				if (messageKey != null) {
					messageKeyList.add(messageKey);
				}
			}
			if (messageKeyList != null && messageKeyList.size() > 0) {
				final String errMsg = "message error exist";
				throw new InputException(new Conveyance(messageKeyList, errMsg));
			}
		}
	}

	/**
	 * メッセージが設定されている場合、例外をスローする。
	 * 
	 * @param messageKeys メッセージ(配列）
	 * @throws InputException メッセージが設定されている場合にスロー
	 */
	public void evaluate(List<MessageKey> messageKeyList) throws InputException {
		if (messageKeyList == null || messageKeyList.size() < 1) {
			return;
		} else {
			final String errMsg = "message error exist";
			throw new InputException(new Conveyance(messageKeyList, errMsg));
		}
	}
}
